using System;
using System.Collections.Generic;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;

namespace ContentOverContainer.Common.Converter
{
    public static class AssignmentConverter
    {
        /// <summary>
        /// Converts the assign expression. (R-Assign1 , R-Assign2)
        /// </summary>
        /// <param name="pAssignExpression">The binary (assign) expression.</param>
        /// <author>Harald Binkle</author>
        public static ChangeNodeInfo ConvertAssignExpression(BinaryExpressionSyntax pAssignExpression, ConverterContext pConverterData)
        {

            if (pConverterData.MultiplicityInfo.Kind != MultiplicityKind.Any)
                return new ChangeNodeInfo(pAssignExpression);//non any case

            var value = pAssignExpression.Right;
            var member = pAssignExpression.Left;

            if (!member.EquivalentTo(pConverterData.NodeTypeInfo.Node))//the annnotated member is the target of the assignment, it's the value
                return new ChangeNodeInfo(pAssignExpression);


            List<ParameterPair> parameter;
            var valueIsAnyAnnotated = ExpressionSyntaxConverter.GatherBinaryExpressionInfo(pConverterData, value, member, out parameter);
            if (!valueIsAnyAnnotated)
            {
                var symbolInfo = pConverterData.SemanticModel.GetSymbolInfo(value);
                TypeSymbol symType = null;
                var localSymbol = symbolInfo.Symbol as LocalSymbol;
                if (localSymbol != null)
                {
                    valueIsAnyAnnotated = localSymbol.Type.AllInterfaces.Any(intf => intf.Name.ToString().Equals("IList"));
                    if (valueIsAnyAnnotated) symType = localSymbol.Type;
                }
                else
                {  //Case needed for initializing properties
                    var newSymbol = symbolInfo.Symbol as MethodSymbol;// SubstitutedMethodSymbol;//object creation syntax
                    if (newSymbol != null)
                    {
                        valueIsAnyAnnotated = newSymbol.ContainingType.AllInterfaces.Any(intf => intf.Name.ToString().Equals("IList"));
                        if (valueIsAnyAnnotated) symType = newSymbol.ContainingType;
                        if (valueIsAnyAnnotated)
                        {
                            var objectcreationStatement = Syntax.ParseExpression(string.Format("{0} = {1}" + Environment.NewLine, member.ToString(), value.ToString()));//R-Assign3
                            return new ChangeNodeInfo(objectcreationStatement);
                        }
                    }
                }
                if (valueIsAnyAnnotated)
                    parameter.ForEach(param => param.Type = Syntax.ParseTypeName(symType.ToMinimalDisplayString(localSymbol.Locations.First(), pConverterData.SemanticModel)));

            }


            //build Clear statement
            var invocationClearStatement = Syntax.ParseStatement(string.Format("{0}.Clear();" + Environment.NewLine, member.ToString()));//R-Assign1
            //build Add statement
            StatementSyntax invocationAddStatement;
            if (valueIsAnyAnnotated)
                invocationAddStatement = Syntax.ParseStatement(string.Format("{0}.AddRange({1}); " + Environment.NewLine, member.ToString(), value.ToString()));//R-Assign2
            else
                invocationAddStatement = Syntax.ParseStatement(string.Format("if({1}!=null) {0}.Add({1});" + Environment.NewLine, member.ToString(), value.ToString()));
            var returnStatement = Syntax.ParseStatement(String.Format("return {0};", member));
            var methodBody = Syntax.Block(invocationClearStatement, invocationAddStatement, returnStatement);

            TypeSyntax exprType = pConverterData.MultiplicityInfo.Type;

            var composition = MethodComposer.BuildMethod("Assign", methodBody, parameter, exprType.ToString());
            var replacement = Syntax.ParseExpression(String.Format("{0} = {1}", member.ToString(), composition.InvocationCall));
            var result = new ChangeNodeInfo(replacement);
            var methodSubstitution = new ChangeNodeMethodSubstitution()
            {
                Class = pAssignExpression.FirstAncestorOrSelf<ClassDeclarationSyntax>(),
                MethodDeclaration = composition.MethodDecl
            };
            result.MethodSubstitutionList.Add(methodSubstitution);
            return result;
        }
    }
}